package cn.stimd.spring.aop.framework;

import cn.stimd.spring.aop.TargetSource;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }


    @Override
    public Object getProxy() {
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        //判断equals和hashcode方法是否定义在被代理的接口（略）
        return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), proxiedInterfaces, this);
    }


    /**
     * 主要工作是将Proxy中的Advisor集合按一定的顺序排列
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class<?> targetClass = null;
        Object target;

        try {
            //前置检查，过滤equals、hashcode等（略）

            //1. 如果exposeProxy属性为true，将代理对象绑定到ThreadLocal上
            Object retVal;
            if (this.advised.exposeProxy) {
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            //获取目标对象和对应的类型
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            //2. 获取拦截器链
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            //3. 执行拦截操作，最终调用目标方法
            //如果拦截器链为空，当作普通方法调用
            if (chain.isEmpty()) {
                ReflectionUtils.makeAccessible(method);
                retVal = method.invoke(target, args);
            }else{
                //拦截器不为空，执行增强逻辑并调用目标方法
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();
            }
            return retVal;
        }finally {
            //解除代理对象与当前线程的绑定
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
}
